#ifndef VEC3_H
#define VEC3_H

#include <math.h>
//#include "RadiansAndDegreesConverter.h"

#ifdef _DIRECTX11
#include <D3DX11.h>
#include <D3DX10math.h>
#endif

template <class T>
struct Vec3
{
	Vec3()
	{
		x = 0;
		y = 0;
		z = 0;
	}

	Vec3(T xT, T yT, T zT) // or : x(xT), y(yT), z(zT) {}
	{
		x = xT;
		y = yT;
		z = zT;
	}

//sum operator
	Vec3& operator+= (const Vec3& rhs)
	{
		x += rhs.x;
		y += rhs.y;
		z += rhs.z;

		return *this;
	}

	Vec3 operator+ (const Vec3& a) const
	{
		Vec3 result = *this;
		result += a;
		return result;
	}
//minus operator

	Vec3& operator-= (const Vec3& rhs)
	{
		x -= rhs.x;
		y -= rhs.y;
		z -= rhs.z;

		return *this;
	}

	Vec3 operator- (const Vec3& rhs) const
	{
		Vec3 result = *this;
		result -= rhs;
		return result;
	}

	Vec3 operator- () const
	{
		return Vec3(-x, -y, -z);
	}
//multiplication operator
	Vec3& operator*= (const T rhs) 
	{
		x *= rhs;
		y *= rhs;
		z *= rhs;

		return *this;
	}

	Vec3 operator* (const T rhs) const
	{
		Vec3 result = *this;
		result *= rhs;
		return result;
	}

	//Vec3 operator* (const float rhs) const
	//{
	//	Vec3 result = *this;
	//	result.x * rhs;
	//	result.y * rhs;
	//	result.z * rhs;
	//	return result;
	//}
	
// dot product
	float operator* (const Vec3 rhs) const
	{
		Vec3 result = *this;
		result.x *= rhs.x;
		result.y *= rhs.y;
		result.z *= rhs.z;
		float returned = result.x + result.y + result.z;
		return returned;
	}
// division operator
	Vec3& operator/= (const T rhs) 
	{
		x /= rhs;
		y /= rhs;
		z /= rhs;

		return *this;
	}

	Vec3 operator/ (const T rhs) const
	{
		Vec3 result = *this;
		result /= rhs;
		return result;
	}

// comparison operators start
	bool operator== (Vec3 rhs)
	{
		return (x == rhs.x && y == rhs.y && z == rhs.z);
	}
	bool operator!= (const Vec3& rhs) const
	{
		return (x != rhs.x && y != rhs.y && z != rhs.z);
	}

// comparison operators end
// magnitude/length
	float SquaredLength() const
	{
		return x*x + y*y + z*z;
	}
	float Length() const
	{
		return sqrtf(x*x + y*y + z*z);
	}
// make non member function
	//float AngleBetween(const Vec3& rhs) const
	//{
	//	// multiply the two vectors
	//	Vec3 th isVec = *this;
	//	Vec3 result = thisVec;

	//	float result2 = result *= rhs;

	//	float angle = 0;
	//	float lhsLength = 0;
	//	float rhsLength = 0;
	//	float total = 0;
	//	// find length
	//	lhsLength = sqrt(thisVec.SquaredLength());
	//	rhsLength = sqrt(rhs.SquaredLength());
	//	//find (u x v) / (|u| x |v|)
	//	float lhs = result2 / (lhsLength * rhsLength);

	//	angle = acos(lhs);

	//	return angle;
	//}

//cross product ( can be non-member func )
	/*Vec3 CrossProduct(const Vec3& rhs) const
	{
		Vec3 thisVec = *this;
		Vec3 result(0, 0, 0);
		result.x = thisVec.y * rhs.z - thisVec.z * rhs.y;
		result.y = thisVec.z * rhs.x - thisVec.x * rhs.z;
		result.z = thisVec.x * rhs.y - thisVec.y * rhs.x;

		return result;
	}*/

// normalise
	Vec3 Normalise()
	{
		Vec3 result = *this;
		T length = sqrt(result * result);
		T oneOver = 1.0f / length;
		result.x *= oneOver;
		result.y *= oneOver;
		result.z *= oneOver;

		*this = result;

		return *this;
	}

	float AngleBetween(const Vec3& u, const Vec3& v)
	{
		// multiply the two vectors
		//Vec3f thisVec = u;
		//Vec3f result = thisVec;
		
		float result = u * v;
		float angle = 0;
		float lhsLength = 0;
		float rhsLength = 0;
		float total = 0;
		// find length
		lhsLength = sqrt(u.SquaredLength());
		rhsLength = sqrt(v.SquaredLength());
		//find (u x v) / (|u| x |v|)
		float lhs = result / (lhsLength * rhsLength);

		angle = acos(lhs);

		return angle;
	}

	Vec3 CrossProduct(const Vec3& u, const Vec3& v)
	{
		//Vec3f thisVec = ;
		Vec3f result(0, 0, 0);
		result.x = u.y * v.z - u.z * v.y;
		result.y = u.z * v.x - u.x * v.z;
		result.z = u.x * v.y - u.y * v.x;

		return result;
	}

	float Dot(const Vec3& rh)
	{
		return (float)x*rh.x + y*rh.y + z*rh.z;
	}

	float Dot(const Vec3& rh, const Vec3& lh)
	{
		return (float)lh.x*rh.x + lh.y*rh.y + lh.z*rh.z;
	}

#ifdef _DIRECTX11
	Vec3 ToVec3(D3DXVECTOR3& rh)
	{
		x = rh.x;
		y = rh.y;
		z = rh.z;
		return *this;
	}
	void ToVec3(const D3DXVECTOR3& rh)
	{
		x = rh.x;
		y = rh.y;
		z = rh.z;
		//rh.x = x;
		//rh.y = y;
		//rh.z = z;
	}
	void ToD3DXVECTOR3(D3DXVECTOR3& rh)
	{
		rh = D3DXVECTOR3(x, y, z, 1);
	}
#endif

	T x, y, z;
};


typedef Vec3<int> Vec3i;
typedef Vec3<float> Vec3f;

//Vec3f CrossProduct(const Vec3f& u, const Vec3f& v)
//{
//	return Vec3f(
//		u.y * v.z - u.z * v.y,
//		u.z * v.x - u.x * v.z,
//		u.x * v.y - u.y * v.x);
//}

#endif VEC3_H



// add comparison operators